package com.wentao.thisCommon.common;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.wentao.thisCommon.annotation.EntityItem;
import com.wentao.thisCommon.annotation.ItemVaild;
import com.wentao.thisCommon.annotation.TypeEntity;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.beans.PropertyDescriptor;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

public class ConvertEntity {
    public static final String CHARSET = "utf-8";

    protected static Gson gson;
    protected static Gson rgson = new GsonBuilder().serializeNulls().create();
    protected static Map<String, List<EntityCollectItem>> entityCollect = new Hashtable();

    public static final String Empty = "";
    public static final String Null = null;
    public static final String Enter = "\r\n";
    public static final String DefaultCoding = "UTF-8";
    public static final String Login = "login";


    public static Gson GetGson() {
        if (null == gson) {
            GsonBuilder builder = new GsonBuilder();
//            builder.registerTypeAdapter(String.class, new GsonConvertString());
//            builder.registerTypeAdapter(Integer.class, new GsonConvertInt());
//            builder.registerTypeAdapter(Long.class, new GsonConvertLong());
//            builder.registerTypeAdapter(Double.class, new GsonConvertDouble());
            gson = builder.create();
        }

        return gson;
    }

    public static <T> T jsonToJava(String json, Class<?> cls) {
        return (T) GetGson().fromJson(json, cls);
    }

    public static <T> T jsonToJava(String json, Type tp) {
        return (T) GetGson().fromJson(json, tp);
    }

    public static String javaToJson(Object obj) {
        return rgson.toJson(obj);
    }

    public static String javaToJsonNullable(Object obj) {
        return GetGson().toJson(obj);
    }

    public static <T, V> T jsonToCopy(V res, Type tar) {
        String json = javaToJson(res);
        return (T) jsonToJava(json, tar);
    }

    public static <T, V> T jsonToCopy(V res, Class tar) {
        String json = javaToJson(res);
        return (T) jsonToJava(json, tar);
    }

    public static String convertToXml(Object obj) {
        return convertToXml(obj, DefaultCoding, false);
    }

    public static Map<String, String> CovertToMap(Object obj) {
        String json = ConvertEntity.javaToJson(obj);
        Type tp = new TypeToken<Map<String, String>>() {
        }.getType();

        Map<String, String> map = ConvertEntity.jsonToJava(json, tp);
        return map;
    }

    public static List<Map<String, String>> CovertToMap(List obj) {
        String json = ConvertEntity.javaToJson(obj);
        Type tp = new TypeToken<List<Map<String, String>>>() {
        }.getType();
        List<Map<String, String>> map = ConvertEntity.jsonToJava(json, tp);

        return map;
    }

    public static String convertToXml(Object obj, String encoding, boolean showFagment) {
        String result = null;
        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            // marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, showFagment);

            StringWriter writer = new StringWriter();
            marshaller.marshal(obj, writer);
            result = writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }

    public static <T> T converyToJavaBean(String xml, Class<T> c) {
        T t = null;
        try {
            JAXBContext context = JAXBContext.newInstance(c);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            t = (T) unmarshaller.unmarshal(new StringReader(xml));
        } catch (Exception e) {
            e.printStackTrace();
        }

        return t;
    }

    public static <T> T converyToJavaBean(String xml, Type t) {
        Class c = GetClass(t);
        return (T) converyToJavaBean(xml, c);
    }

    public static List copyEntityArray(List<?> res, Class<?> tar) throws Exception {
        return copyEntityArray(res, tar, null);
    }

    public static List copyEntityArray(List<?> res, Class<?> tar, ConvertValueItem val) throws Exception {
        List list = new ArrayList();
        for (Object obj : res) {
            list.add(copyEntity(obj, tar.newInstance(), val));
        }

        return list;
    }

    public static Object copyEntity(Object res, Type tar) throws Exception {
        Class c = GetClass(tar);
        return copyEntity(res, c);
    }

    public static Object copyEntity(Object res, Class<?> tar) throws Exception {
        return copyEntity(res, tar, null);
    }

    public static Object copyEntity(Object res, Class<?> tar, ConvertValueItem val) throws Exception {
        if (res instanceof List) {
            return copyEntityArray((List<?>) res, tar, val);
        }

        return copyEntity(res, tar.newInstance(), val);
    }

    public static <T, V> T copyEntity(V res, T tar, ConvertValueItem val) throws Exception {

        Class<? extends Object> resCls = res.getClass();
        Class<? extends Object> tarCls = tar.getClass();

        List<EntityCollectItem> resCollectItem = GetEntityCollectItem(resCls);
        List<EntityCollectItem> tarCollectItem = GetEntityCollectItem(tarCls);

        for (EntityCollectItem tarItem : tarCollectItem) {
            Object value = null;
            for (EntityCollectItem resItem : resCollectItem) {
                if (resItem.getName().equalsIgnoreCase(tarItem.isAlias() ? tarItem.getAlias() : tarItem.getName())) {
                    value = resItem.getReadMethod().invoke(res);
                    break;
                }
            }

            if (null != val) {
                value = val.convertValue(value, res, tarItem);
            }
            if (null != value) {
                if (tarItem.isChildType()) {
                    value = copyEntity(value, tarItem.getChildType());
                }

                tarItem.getWriteMethod().invoke(tar, new Object[]{value});
            }
        }

        return tar;
    }

    public static <T, V> T copyEntity(V res, T tar) throws Exception {
        return copyEntity(res, tar, null);
    }

    public static <T, V> boolean CompareValue(T res, V tar, boolean nullValue) throws Exception {
        if (null == res || tar == null) {
            return res == tar;
        }

        List<EntityCollectItem> resCollectItem = GetEntityCollectItem(res.getClass());
        List<EntityCollectItem> tarCollectItem = GetEntityCollectItem(tar.getClass());

        for (EntityCollectItem tarItem : resCollectItem) {
            for (EntityCollectItem resItem : tarCollectItem) {
                if (resItem.getName().equalsIgnoreCase(tarItem.isAlias() ? tarItem.getAlias() : tarItem.getName())) {
                    Object resvalue = resItem.getReadMethod().invoke(res);
                    Object tarvalue = tarItem.getReadMethod().invoke(tar);
                    if (null == resvalue && nullValue) {
                        if (null != tarvalue) {
                            return false;
                        }
                    } else if (null == resvalue) {
                    } else if (resItem.isChildType()) {
                        if (!CompareValue(resvalue, tarvalue, nullValue)) {
                            return false;
                        }
                    } else if (!resvalue.toString().equals(tarvalue + ConvertEntity.Empty)) {
                        return false;
                    }

                    break;
                }
            }
        }

        return true;
    }

    public static <T> T SetPropertyNullValue(T res) {
        Class<? extends Object> resCls = res.getClass();
        T tar = null;
        try {
            tar = (T) resCls.newInstance();
            List<EntityCollectItem> resCollectItem = GetEntityCollectItem(resCls);
            for (EntityCollectItem resItem : resCollectItem) {
                Object values = resItem.getReadMethod().invoke(res);
                if (null != values && values.toString().length() > 0) {
                    resItem.getWriteMethod().invoke(tar, new Object[]{values});
                }
            }
        } catch (Exception e) {

        }

        return tar;
    }

    public static List<EntityCollectItem> GetEntityCollectItem(Class cls) throws Exception {
        List<EntityCollectItem> collectItem;
        if (!entityCollect.containsKey(cls.getName())) {
            entityCollect.put(cls.getName(), createEntityCollectItem(cls));
        }

        collectItem = entityCollect.get(cls.getName());

        return collectItem;
    }

    public static VaildResult VaildEntity(Object entity) throws Exception {
        if (entity instanceof Map) {
            return null;
        }

        Class<? extends Object> cls = entity.getClass();
        if (IsPrimitive(entity)) {
            return null;
        }
        List<EntityCollectItem> collectItem = GetEntityCollectItem(cls);
        VaildResult rtn = null;
        for (EntityCollectItem item : collectItem) {
            Object readValue = item.getReadMethod().invoke(entity);
            if (null == readValue || IsPrimitive(readValue)) {
                VaildResult vaildResult = new VaildResult(item.getName());
                vaildResult.setMessage(item.getItemVaild().VaildMessage());
                if (!item.isNull() && null == readValue) {
                    vaildResult.setCheckNull(false);
                    rtn = vaildResult;
                } else if (!item.isEmpty() && readValue.toString().trim().length() == 0) {
                    vaildResult.setCheckEmpty(false);
                    rtn = vaildResult;
                } else if (item.isRegular() && readValue.toString().matches(item.getRegular())) {
                    vaildResult.setCheckReg(false);
                    rtn = vaildResult;
                } else if (item.getItemVaild().ValueList().length > 0) {
                    vaildResult.setCheckReg(false);
                    rtn = vaildResult;
                    for (String value : item.getItemVaild().ValueList()) {
                        if (value.equals(readValue + ConvertEntity.Empty)) {
                            vaildResult.setCheckReg(true);
                            rtn = null;
                            break;
                        }
                    }
                }
            } else if (readValue instanceof List) {
                for (Object obj : (List) readValue) {
                    VaildResult vrtn = VaildEntity(obj);
                    if (null != vrtn) {
                        rtn = vrtn;
                        break;
                    }
                }
            } else if (readValue instanceof Map) {
                continue;
            } else {
                VaildResult vrtn = VaildEntity(readValue);
                rtn = vrtn;
            }

            if (null != rtn) {
                return rtn;
            }
        }

        return null;
    }

    public static boolean IsNullOrEmpty(Object obj) {
        if (null == obj) {
            return true;
        }

        return !(obj.toString().length() > 0);
    }


    protected static List<EntityCollectItem> createEntityCollectItem(Class cls) throws Exception {
        Field[] ownFields = cls.getDeclaredFields();
        Field[] supperFields = cls.getSuperclass().getDeclaredFields();

        List<Field> fields = new ArrayList<Field>();

        for (Field f : ownFields) {
            fields.add(f);
        }

        for (Field f : supperFields) {
            fields.add(f);
        }


        List<EntityCollectItem> list = new ArrayList();
        for (Field f : fields) {
            EntityCollectItem item = new EntityCollectItem();

            item.setName(f.getName());
            if (f.isAnnotationPresent(EntityItem.class)) {
                item.setAlias(f.getAnnotation(EntityItem.class).itemName());
                item.setAlias(true);
            }
            if (f.isAnnotationPresent(ItemVaild.class)) {
                String reg = f.getAnnotation(ItemVaild.class).regular();
                item.setRegular(!reg.isEmpty());
                item.setRegular(reg);
                item.setNull(f.getAnnotation(ItemVaild.class).isNull());
                item.setEmpty(f.getAnnotation(ItemVaild.class).isEmpty());

                int nullOrEmpty = f.getAnnotation(ItemVaild.class).NullOrEmpty();

                if (nullOrEmpty > 0) {
                    item.setNull(!((nullOrEmpty & StringEmptyNull.NULL) == StringEmptyNull.NULL));
                    item.setEmpty(!((nullOrEmpty & StringEmptyNull.EMPTY) == StringEmptyNull.EMPTY));
                }

                item.setItemVaild(f.getAnnotation(ItemVaild.class));
            }

            if (f.isAnnotationPresent(TypeEntity.class)) {
                item.setChildType(f.getType());
            }

            try {
                PropertyDescriptor respd = new PropertyDescriptor(f.getName(), cls);
                item.setReadMethod(respd.getReadMethod());
                item.setWriteMethod(respd.getWriteMethod());
                list.add(item);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return list;
    }

    public static void SetEntityPropertyValue(Object entity, String path, Object value) throws Exception {
        if (entity instanceof List) {
            for (Object obj : (List) entity) {
                SetPropertyValue(obj, path, value);
            }
        } else {
            SetPropertyValue(entity, path, value);
        }
    }

    public static List GetEntityPropertyValue(Object entity, String path) throws Exception {
        List list = new ArrayList();
        GetEntityPropertyValue(entity, path, list);
        return list;
    }

    public static Class GetClass(Type type) {
        return GetClass(type, 0);
    }

    protected static Class GetClass(Type type, int i) {
        if (type instanceof ParameterizedType) { // 处理泛型类型
            return GetGenericClass((ParameterizedType) type, i);
        } else if (type instanceof TypeVariable) {
            return (Class) GetClass(((TypeVariable) type).getBounds()[0], 0); // 处理泛型擦拭对象
        } else {// class本身也是type，强制转型
            return (Class) type;
        }
    }

    protected static Class GetGenericClass(ParameterizedType parameterizedType, int i) {
        Object genericClass = parameterizedType.getActualTypeArguments()[i];
        if (genericClass instanceof ParameterizedType) { // 处理多级泛型
            return (Class) ((ParameterizedType) genericClass).getRawType();
        } else if (genericClass instanceof GenericArrayType) { // 处理数组泛型
            return (Class) ((GenericArrayType) genericClass).getGenericComponentType();
        } else if (genericClass instanceof TypeVariable) { // 处理泛型擦拭对象
            return (Class) GetClass(((TypeVariable) genericClass).getBounds()[0], 0);
        } else {
            return (Class) genericClass;
        }
    }

    protected static void GetEntityPropertyValue(Object entity, String path, List value) throws Exception {
        if (entity instanceof List) {
            for (Object obj : (List) entity) {
                GetPropertyValue(obj, path, value);
            }
        } else {
            GetPropertyValue(entity, path, value);
        }
    }

    protected static void SetPropertyValue(Object entity, String path, Object value) throws Exception {
        int index = path.indexOf(".");
        String paths = path.substring(0, index);
        String othPaths = path.substring(index + 1);
        Class<? extends Object> cls = entity.getClass();
        List<EntityCollectItem> collectItem = GetEntityCollectItem(cls);
        for (EntityCollectItem item : collectItem) {
            if (paths.equalsIgnoreCase(item.isAlias() ? item.getAlias() : item.getName())) {
                int nextIndex = othPaths.indexOf(".");
                if (nextIndex > 0) {
                    SetEntityPropertyValue(item.getReadMethod().invoke(entity), othPaths, value);
                } else {
                    item.getWriteMethod().invoke(entity, value);
                }

                break;
            }
        }
    }

    protected static void GetPropertyValue(Object entity, String path, List values) throws Exception {
        int index = path.indexOf(".");
        String paths = path.substring(0, index);
        String othPaths = path.substring(index + 1);
        Class<? extends Object> cls = entity.getClass();
        List<EntityCollectItem> collectItem = GetEntityCollectItem(cls);
        for (EntityCollectItem item : collectItem) {
            if (paths.equalsIgnoreCase(item.isAlias() ? item.getAlias() : item.getName())) {
                int nextIndex = othPaths.indexOf(".");
                if (nextIndex > 0) {
                    GetEntityPropertyValue(item.getReadMethod().invoke(entity), othPaths.substring(nextIndex), values);
                } else {
                    values.add(item.getReadMethod().invoke(entity));
                }
            }
        }
    }

    protected static boolean IsPrimitive(Object obj) {
        return obj.getClass().isPrimitive() || obj instanceof Integer || obj instanceof Double || obj instanceof Boolean
                || obj instanceof Character || obj instanceof Byte || obj instanceof Short || obj instanceof Long
                || obj instanceof Float || obj instanceof Void || obj instanceof String;
    }


}
